/*
 * Copyright (c) 2010, Johan T. Larsson All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Mercenary Commander project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *      
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL JOHAN T. LARSSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package games.TBSGame;

import java.util.Timer;
import java.util.TimerTask;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.Window;

public class BattleSurfaceView extends GLSurfaceView {

	public BattleRenderer Renderer;
	BattleWorld World;
	ResourceManager ResourceManager;
	
	//Touch input state machine graph:
	//State 0: No input
	//State 0 -> State 1 when touched
	//State 1 -> State 0 when released - generate "click"
	//State 1 -> State 2 after X time - generate "hold"
	//State 1 -> State 3 if move - generate "drag"
	//State 2 -> State 2 if move - generate "drag-and-hold"
	//State 2 -> State 0 if release - generate "drop"
	//State 3 -> State 3 if move - generate "drag"
	//State 3 -> State 0 if release
	private float LastInputX = 0;
	private float LastInputY = 0;
	private float InputTolerance = 10;
	private int InputState = 0;
	private final int InputDelay = 250;
	private Timer InputTimer;
	private float MultiTouchDist = 10;
	private boolean MultiTouch = false;

	public int MoveMode = 0;
	
	public BattleSurfaceView(Context context, AttributeSet attribs) {
		super(context, attribs);
		ResourceManager = games.TBSGame.ResourceManager.Singleton;
	}
	
	public void Initialize(BattleWorld world)
	{
		World = world;
		Renderer = new BattleRenderer(ResourceManager, World);
		setRenderer(Renderer);
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
	}

    @Override 
    public boolean onTouchEvent(MotionEvent e) {
    	if(e.getPointerCount() > 1)
    	{
        	float x1 = e.getX(0);
        	float y1 = e.getY(0);
        	float x2 = e.getX(1);
        	float y2 = e.getY(1);
       		float distX = x1 - x2;
       		float distY = y1 - y2;
       		float dist = (float) Math.sqrt(distX*distX + distY*distY);
       		if(dist > 50 && (InputState == 0 || InputState == 1))
       		{
       			MultiTouch = true;
       			if(InputTimer != null) {
       				InputTimer.cancel();
       				InputTimer = null;
       			}
       		}
    	}
    	if(MultiTouch)
    	{
    		return OnMultiTouchEvent(e);
    	}
    	
    	float dX = e.getX() - LastInputX;
    	float dY = e.getY() - LastInputY;
    	LastInputX += dX;
    	LastInputY += dY;
    	
    	synchronized (Renderer)
    	{
	    	switch (e.getAction()) {
	    	case MotionEvent.ACTION_DOWN:
	    		if(InputState == 0)
	    		{
	    			InputState = 1;
	    			//TODO: Does this leak?
	    			InputTimer = new Timer();
	    			InputTimer.schedule(new TimerTask() {
	    	            public void run() {
	    	            	InputTimeout();
	    	                InputTimer.cancel();
	    	            }
	    			}, InputDelay);
	    		}
	    		break;
	    	case MotionEvent.ACTION_MOVE:
	    		switch(InputState) {
	    		case 1:
	    			if(Math.abs(dX) > InputTolerance || Math.abs(dY) > InputTolerance)
	    			{
	    	    		if(InputTimer != null) {
	           				InputTimer.cancel();
	           				InputTimer = null;
	           			}
	    				InputState = 3;
	    				Renderer.Drag(dX, dY);
	    			}
	    			else 
	    			{
	    				LastInputX -= dX;
	    				LastInputY -= dY;
	    			}
	    			break;
	    		case 2:
	    			if(InputTimer != null) {
           				InputTimer.cancel();
           				InputTimer = null;
           			}
	    			Renderer.DragAndHold(LastInputX, LastInputY);
	    			break;
	    		case 3:
	    			if(InputTimer != null) {
           				InputTimer.cancel();
           				InputTimer = null;
           			}
	    			Renderer.Drag(dX, dY);
	    			break;
	    		}
	    		break;
	    	case MotionEvent.ACTION_UP:
	    		if(InputTimer != null) {
       				InputTimer.cancel();
       				InputTimer = null;
       			}
	    		switch(InputState){
	    		case 1:
	    			InputState = 0;
	    			Renderer.Click(LastInputX, LastInputY);
	    			break;
	    		case 2:
	    			InputState = 0;
	    			Renderer.Drop(LastInputX, LastInputY, MoveMode);
	    			break;
	    		case 3:
	    			InputState = 0;
	    			break;
	    		case 4:
	    			InputState = 0;
	    			break;
	    		}
	    		break;
	    	case MotionEvent.ACTION_CANCEL:
	    		if(InputTimer != null) {
       				InputTimer.cancel();
       				InputTimer = null;
       			}
	    		InputState = 0;
	    		MultiTouch = false;
	    		break;
	    	}
	    	requestRender();
    	}
        return true;
    }
    
    private void InputTimeout() {
    	if(MultiTouch)
    		return;
    	if(InputState == 1)
    	{
    		InputState = 2;
    		synchronized (Renderer) {
    			Renderer.Hold(LastInputX, LastInputY);
    			requestRender();
    		}
    	}
    }
    
    public boolean OnMultiTouchEvent(MotionEvent e) {
    	float dX = e.getX() - LastInputX;
    	float dY = e.getY() - LastInputY;
    	LastInputX += dX;
    	LastInputY += dY;
    	
    	float distX = 0;
    	float distY = 0;
    	float curDist = MultiTouchDist;
    	if(e.getPointerCount() > 1)
    	{
    		distX = LastInputX - e.getX(1);
    		distY = LastInputY - e.getY(1);
    		curDist = (float) Math.sqrt(distX*distX + distY*distY);
    		if(curDist < 10)
    			curDist = MultiTouchDist;
    	}
    	
    	synchronized (Renderer)
    	{
	    	switch (e.getAction()) {
	    	case MotionEvent.ACTION_DOWN:
	    		if(MultiTouch)
	    		{
	    			if(InputState == 0 || InputState == 1 || InputState == 3 || InputState == 4)
	    			{
		    			InputState = 4;
		        		MultiTouchDist = curDist;
	    			}
	    		}
	    	case MotionEvent.ACTION_MOVE:
	    		if(e.getPointerCount() > 1)
	    		{	        		
	        		if(InputState == 4)
	        		{
	        			if(Math.abs(curDist - MultiTouchDist) > 1.0f)
	        			{
	        				Renderer.Zoom(curDist/MultiTouchDist);
	        				MultiTouchDist = curDist;
	        			}
	    			}
	        		else if(InputState != 2)
	        		{
		        		MultiTouchDist = curDist;
	        			
	        			InputState = 4;
	        		}
	    		}
	    	case MotionEvent.ACTION_UP:
    			if(e.getPointerCount() < 2) {
    				MultiTouch = false;
    				InputState = 0;
    			}
    			break;
	    	}
	    	requestRender();
    	}
        return true;
    }

	public int SwitchMoveMode() {
		MoveMode++;
		if(MoveMode > 1)
			MoveMode = 0;
		
		return MoveMode;
	}

	public void RestoreState(Bundle state)
	{
		MoveMode = state.getInt("UI_MOVEMODE");
		Renderer.RestoreState(state);
	}

	public void SaveState(Bundle state)
	{
		state.putInt("UI_MOVEMODE", MoveMode);
		Renderer.SaveState(state);
	}
}
